<?php

/**
 * 格式化数组
 * Class Tree
 */
class Tree
{
    /**
     * @var array 原始分类数据
     */
    protected $rawList = [];
    /**
     * @var string 主键字段
     */
    protected $pkField = 'id';
    /**
     * @var string 上级分类字段
     */
    protected $parentField = 'pid';
    /**
     * @var string 生成数据下级分类存放key
     */
    protected $childrenField = 'children';
    /**
     * @var bool 是否以扁平化数据输出
     */
    protected $flat = false;
    /**
     * @var array|mixed|string 显示字段
     */
    protected $showFields = ['*'];
    /**
     * @var string 名称
     */
    protected $name = 'name';
    /**
     * @var string 填充名称
     */
    protected $padName = 'pad_name';
    /**
     * @var int 当前级别
     */
    protected $level = 1;
    /**
     * @var string 当前分类路径
     */
    protected $path = '';
    /**
     * @var string 路径分隔符
     */
    protected $pathDelimiter = ',';
    /**
     * @var array 当前元素
     */
    protected $current = [];
    /**
     * @var array 返回数据
     */
    protected $returnArray = [];

    /**
     * @var string 错误信息
     */
    protected $error = '';

    protected static $cacheArray = [];

    /**
     * @var array 显示图标
     */
    protected $icon = ['|','|---'];

    public function __construct($config = [])
    {
        !empty($config['pk']) && $this->setPkField($config['pk']);
        !empty($config['delimiter']) && $this->setPathDelimiter($config['delimiter']);
        !empty($config['flat']) && $this->setFlat($config['flat']);
        !empty($config['pid']) && $this->setParentField($config['pid']);
        !empty($config['children']) && $this->setChildrenField($config['children']);

        !empty($config['fields']) && $this->setShowFields($config['fields']);
        !empty($config['name']) && $this->setName($config['name']);
        !empty($config['pad_name']) && $this->setPadName($config['pad_name']);
    }

    // 获取树状
    public function getTree($array=[], $pid=0, $level=1)
    {
        static::$cacheArray = [];
        $this->setRawList($array);
        $this->returnArray = [];
        $path = $pid.$this->pathDelimiter;
        if ($this->flat) {
            // 获取扁平化的数据
            $this->returnArray = $this->getFlatTree($pid, $level, $path);
        } else {
            // 获取带children的数据
            $this->returnArray = $this->getSunTree($pid, $level, $path);
        }

        return $this->returnArray;
    }

    /**
     * 获取扁平化的数据
     * @param int    $pid
     * @param int    $level
     * @param string $path
     *
     * @return array
     */
    protected function getFlatTree($pid=0, $level=1, $path='')
    {
//        static $returnArray = [];
        foreach($this->rawList as $k=>$v) {
            if ($v[$this->parentField] == $pid) {
                $v = $this->filterItem($v, $level, $path);
                static::$cacheArray[] = $v;
                $this->getFlatTree($v[$this->pkField],$level+1, $v['path'].$v['id'].$this->pathDelimiter);
            }
        }

        return static::$cacheArray;
    }

    /**
     * 获取子孙树
     *
     * @param int    $pid
     * @param int    $level
     * @param string $path
     *
     * @return array
     */
    protected function getSunTree($pid=0, $level=1, $path='')
    {
        $tree = [];
        $items = $this->getFlatTree($pid, $level, $path);
        $items = array_column($items, null, $this->pkField);
        foreach($items as $k=>$v) {
            if ($v[$this->parentField] == $pid) {
                $tree[] = &$items[$v[$this->pkField]];
            } else {
                if (isset($items[$v[$this->parentField]])) {
                    $items[$v[$this->parentField]][$this->childrenField][] = &$items[$v[$this->pkField]];
                }
            }
        }

        return $tree;
    }

    /**
     * @param $item
     * @param $level
     * @param $path
     *
     * @return mixed
     */
    private function filterItem($item, $level, $path)
    {
        if (!in_array('*', $this->showFields)) {
            foreach($item as $k=>$v) {
                if(!in_array($k, $this->showFields)) {
                    unset($item[$k]);
                }
            }
        }
        $pre = '';
        if ($level>1) {
            $pre = $this->icon[0].str_repeat('&nbsp;', ($level-1)*4);
        }
        $item[$this->padName] = $pre.$this->icon[1].$item[$this->name];
        $item['level'] = $level;
        $item['path'] = $path;

        return $item;
    }

    /**
     * @param string $pkField
     */
    public function setPkField($pkField)
    {
        $this->pkField = $pkField;
    }

    /**
     * @param string $parentField
     */
    public function setParentField($parentField)
    {
        $this->parentField = $parentField;
    }

    /**
     * @param string $childrenField
     */
    public function setChildrenField($childrenField)
    {
        $this->childrenField = $childrenField;
    }

    /**
     * @param array $rawList
     */
    public function setRawList($rawList)
    {
        $this->rawList = $rawList;
    }

    /**
     * @param array|mixed|string $showFields
     */
    public function setShowFields($showFields)
    {
        $showFields = is_array($showFields) ? $showFields : explode(',', $showFields);
        $showFields = array_merge($showFields, [$this->pkField, $this->parentField]);
        $this->showFields = $showFields;
    }

    /**
     * @param string $pathDelimiter
     */
    public function setPathDelimiter($pathDelimiter)
    {
        $this->pathDelimiter = $pathDelimiter;
    }

    /**
     * @param bool $flat
     */
    public function setFlat($flat)
    {
        $this->flat = $flat;
    }

    /**
     * @param string $name
     */
    public function setName($name)
    {
        $this->name = $name;
    }

    /**
     * @param string $padName
     */
    public function setPadName($padName)
    {
        $this->padName = $padName;
    }

    /**
     * @return string
     */
    public function getError()
    {
        return $this->error;
    }
}